/* Copyright 2014 Pasi J. Elo, blackdogspark@hotmail.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. 
 */

using Microsoft.SPOT;
using PJE.GlideEx.Extensions;
using System;
using System.Collections;
using System.IO;
using System.Xml;

namespace PJE.GlideEx
{
	public static class Settings
	{
		private const string UserSettingsSection = "userSettings";
		private const string Setting = "setting";
		private const string Name = "name";
		private const string Type = "type";
		private const string Value = "value";

		private static readonly Hashtable AppSettings;

		static Settings()
		{
			AppSettings = new Hashtable();
		}

		/// <summary>
		/// Get setting "key"
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public static object GetSetting(string key)
		{
			return GetSetting(key, null);
		}

		/// <summary>
		/// Get setting "key" return default value if "key" does not exist
		/// </summary>
		/// <param name="key"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		public static object GetSetting(string key, object defaultValue)
		{
			return AppSettings.Contains(key) ? AppSettings[key] : defaultValue;
		}

		/// <summary>
		/// Set string.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, string value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set string array.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, string[] value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set boolean.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, bool value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set int.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, int value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set int array
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, int[] value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set long.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, long value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set long array.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, long[] value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set double.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, double value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set double array.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, double[] value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set short.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, short value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set short array.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, short[] value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// Set datetime.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public static void SetSetting(string key, DateTime value)
		{
			AppSettings[key] = value;
		}

		/// <summary>
		/// xmlStream: The xml file stream to load.
		/// </summary>
		/// <param name="xmlStream"></param>
		public static void Load(Stream xmlStream)
		{
			var reader = XmlReader.Create(xmlStream);

			AppSettings.Clear();

			try
			{
				while (reader.Read())
				{
					switch (reader.Name)
					{
						case UserSettingsSection:
							while (reader.Read())
							{
								if (reader.Name == UserSettingsSection)
									break;

								if (reader.Name != Setting) continue;
								var key = reader.GetAttribute(Name);
								var value = reader.GetAttribute(Value);
								var type = reader.GetAttribute(Type);

								ValueFromString(key, type, value);
							}
							break;
					}
				}
			}
			catch (Exception e)
			{
				Debug.Print(e.Message);
			}
			finally
			{
				if (reader != null)
					reader.Dispose();
			}
		}

		/// <summary>
		/// xmlStream: The xml file stream to save.
		/// </summary>
		/// <param name="xmlStream"></param>
		public static void Save(Stream xmlStream)
		{
			var writer = new StreamWriter(xmlStream);
			//using (var writer = new StreamWriter(xmlStream))
			try
			{
				writer.Write(""); // Clear the file first.
				writer.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
				writer.WriteLine("<configuration>");
				writer.WriteLine("\t<userSettings>");

				foreach (DictionaryEntry item in AppSettings)
				{
					var itemValue = ValueToString(item);
					var itemType = item.Value.GetType().Name;

					var add = "\t\t<" + Setting + " " + Name + "=\"" + item.Key + "\" type=\"" + itemType + "\" value=\"" + itemValue + "\" />";
					writer.WriteLine(add);
				}
				writer.WriteLine("\t</userSettings>");
				writer.WriteLine("</configuration>");
				writer.Flush();
			}
			catch (Exception e)
			{
				Debug.Print(e.Message);
			}
			finally
			{
				if (writer != null)
					writer.Dispose();
			}
		}

		private static void ValueFromString(string key, string type, string value)
		{
			switch (type)
			{
				case "String":
					AppSettings.Add(key, value);
					Debug.Print("string: " + value);
					break;
				case "String[]":
					AppSettings.Add(key, value.Split('|'));
					Debug.Print("string[]: " + value);
					break;
				case "Int16":
					AppSettings.Add(key, short.Parse(value));
					Debug.Print("int16: " + value);
					break;
				case "Int16[]":
					AppSettings.Add(key, value.ToShortArray());
					Debug.Print("int16[]: " + value);
					break;
				case "Int32":
					AppSettings.Add(key, int.Parse(value));
					Debug.Print("int32: " + value);
					break;
				case "Int32[]":
					AppSettings.Add(key, value.ToIntArray());
					Debug.Print("int32[]: " + value);
					break;
				case "Int64":
					AppSettings.Add(key, long.Parse(value));
					Debug.Print("int64: " + value);
					break;
				case "Int64[]":
					AppSettings.Add(key, value.ToLongArray());
					Debug.Print("int64[]: " + value);
					break;
				case "Double":
					AppSettings.Add(key, double.Parse(value));
					Debug.Print("double: " + value);
					break;
				case "Double[]":
					AppSettings.Add(key, value.ToDoubleArray());
					Debug.Print("double[]: " + value);
					break;
				case "Object":
					AppSettings.Add(key, value);
					Debug.Print("double: " + value);
					break;
				case "Boolean":
					AppSettings.Add(key, value == bool.TrueString);
					Debug.Print("boolean: " + value);
					break;
				default:
					AppSettings.Add(key, value);
					Debug.Print("dafault: " + value);
					break;
			}
		}

		private static string ValueToString(DictionaryEntry dictionaryEntry)
		{
			var str = "";
			switch (dictionaryEntry.Value.GetType().Name)
			{
				case "Int16":
					Debug.Print("int16");
					str = dictionaryEntry.Value.ToString();
					break;
				case "Int16[]":
					Debug.Print("int16[]");
					var i16 = (Int16[])dictionaryEntry.Value;
					str = i16.Join();
					break;
				case "Int32":
					Debug.Print("int32");
					str = dictionaryEntry.Value.ToString();
					break;
				case "Int32[]":
					Debug.Print("int32[]");
					var i32 = (Int32[])dictionaryEntry.Value;
					str = i32.Join();
					break;
				case "Int64":
					Debug.Print("int64");
					str = dictionaryEntry.Value.ToString();
					break;
				case "Int64[]":
					Debug.Print("int64[]");
					var i64 = (Int64[])dictionaryEntry.Value;
					str = i64.Join();
					break;
				case "Double":
					Debug.Print("double");
					str = dictionaryEntry.Value.ToString();
					break;
				case "Double[]":
					Debug.Print("double[]");
					var d = (double[])dictionaryEntry.Value;
					str = d.Join();
					break;
				case "String":
					Debug.Print("string");
					str = dictionaryEntry.Value.ToString();
					break;
				case "String[]":
					Debug.Print("string[]");
					var s = (string[])dictionaryEntry.Value;
					str = s.Join();
					break;
				case "Boolean":
					Debug.Print("boolean");
					str = (bool)dictionaryEntry.Value ? "True" : "False";
					break;
				case "DateTime":
					Debug.Print("datetime");
					str = dictionaryEntry.Value.ToString();
					break;
			}
			return str;
		}
	}
}
